home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************\
- * This is a part of the Microsoft Source Code Samples.
- * Copyright (C) 1993-1996 Microsoft Corporation.
- * All rights reserved.
- * This source code is only intended as a supplement to
- * Microsoft Development Tools and/or WinHelp documentation.
- * See these sources for detailed information regarding the
- * Microsoft samples programs.
- \******************************************************************************/
- /* Include this file first to prevent inclusion of winsock.h by windows.h*/
- #include <winsock2.h>
- #include <windows.h>
- #include <ws2tcpip.h> /*tcpip specific options*/
- #include <tchar.h>
- #include <stdio.h>
- #include <string.h>
-
-
- /**************************** CONSTANTS ***************************************/
-
- /* Size of the buffer to receive messages from peers */
- #define BUF_SIZE 2048
- /* Width of the mini-console allocated for each multicast address */
- #define MINI_COLS 24
- /* Number of console rows reserved for header */
- #define HEADER_ROWS 2
- /* Number of console rows reserved for input */
- #define INPUT_ROWS 2
- /* Zero coordinates */
- const COORD Zero = {0, 0};
-
- #define HELP_MSG TEXT ("Type a message and press <Enter> to send it on all sessions, CTRL\\{Z | C} exits.")
- #define HELP_MSG_LEN (sizeof(HELP_MSG)/sizeof(CHAR)-1)
-
- /**************************** STRUCTURES **************************************/
-
- /* Context associated with each mutlicast session */
- typedef struct _MCAST_CONTEXT MCAST_CONTEXT, *PMCAST_CONTEXT;
- struct _MCAST_CONTEXT {
- PMCAST_CONTEXT next; /* Next record in the list */
- COORD coord; /* Current screen coordinates */
- LPTSTR str; /* User address argument string */
- LPTSTR ifstr; /* Interface address string (if specified) */
-
- SOCKET s; /* Multicast socket */
- struct in_addr addr; /* Multicast address to join in */
- SHORT port; /* Port on which to join */
- struct in_addr ifad; /* Interface to register multicast address */
-
- struct sockaddr_in from; /* Peer address */
- DWORD fromlen;/* Peer address length */
- WSAOVERLAPPED ovlp; /* Overlapped (for asyncronous IO) */
- char buf[BUF_SIZE];/* Buffer to receive peer messages */
- } ;
-
-
- /****************** INTERNAL FUNCTION PROTOTYPES ******************************/
-
- int
- CheckWinsockVersion (
- VOID
- );
-
- int
- ParseAddress (
- LPTSTR argv,
- PMCAST_CONTEXT *ctx
- );
-
- int
- JoinSession (
- PMCAST_CONTEXT ctx
- );
-
- int
- PostReceiveRequest (
- PMCAST_CONTEXT ctx
- );
-
- void CALLBACK
- RecvCompletion(
- IN DWORD dwError,
- IN DWORD cbTransferred,
- IN LPWSAOVERLAPPED lpOverlapped,
- IN DWORD dwFlags
- );
-
-
- BOOL CALLBACK
- CtrlHandler(
- DWORD dwCtrlType
- );
-
- VOID
- WriteMiniConsoleA (
- IN OUT COORD *coord,
- LPSTR output,
- DWORD len
- );
-
- VOID
- PaintScreen (
- VOID
- );
-
- DWORD WINAPI
- InputThread (
- LPVOID param
- );
-
- /**************************** GLOBALS *****************************************/
-
- /* List of multicast session contexts */
- PMCAST_CONTEXT CtxList;
- /* Number of multicast sessions */
- int NumSessions;
- /* Number of pending receive requests (so we can wait till they complete before
- * exiting the thread in which we posted them).*/
- LONG NumPendingRcvs = 0;
- /* Event to be signal when we want to stop and exit */
- HANDLE StopEvent;
- /* Console handles */
- HANDLE HStdout, HStdin;
- /* Handle of the input thread */
- HANDLE HInputThread;
- /* Console geometry */
- CONSOLE_SCREEN_BUFFER_INFO StdScreen, /* Used by this program */
- OldScreen; /* Preserved to restore on exit */
- /* Options with their initial values */
- BOOL Loopback = TRUE;
- BOOL UseWSAJoin = TRUE;
- BOOL ReuseAddress = TRUE;
- int McastTTL = 0;
-
- /*********************************************************************
- * FUNCTION: _tmain *
- * *
- * PURPOSE: Program main. Sets up the console and starts asyncronous*
- * network packet and user input processing, then waits for *
- * signal to cleanup and exit. *
- * *
- * INPUT: number of arguments read from command line and array of *
- * arguments themselves. *
- * *
- * RETURNS: 0 if everything goes OK, error code if something fails. *
- * *
- *********************************************************************/
- int _cdecl
- _tmain (
- int argc,
- TCHAR *argv[]
- ) {
- int err=0; /* Error code */
- DWORD idThread;/* Input thread ID */
- PMCAST_CONTEXT ctx, temp; /* Current context */
- int i; /* Variable to loop thru command line arguments */
-
- /* First get us true console handles (event if parent process
- has redirected them) */
- HStdout = CreateFile (TEXT ("CONOUT$"),
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- 0, NULL);
- HStdin = CreateFile (TEXT ("CONIN$"),
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- 0, NULL);
-
- if ((HStdout==INVALID_HANDLE_VALUE)
- || (HStdin==INVALID_HANDLE_VALUE)
- || !GetConsoleScreenBufferInfo (HStdout, &OldScreen))
- /* If any of the above fails, we won't be able to operate anyway */
- return -1;
-
- if ((argc<=1) || (_tcscmp(argv[1], TEXT ("-?"))==0)) {
- /* Shortcut to let user see the proper command-line format
- without going thru socket DLL loading and fancy screen
- setup */
- goto Usage;
- }
-
- /* Don't let user interrupt us while we are setting up */
- SetConsoleCtrlHandler (NULL, TRUE);
- /* Ensure acceptable input and output console mode */
- SetConsoleMode (HStdout, ENABLE_PROCESSED_OUTPUT);
- SetConsoleMode (HStdin,
- ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT);
-
- /* Verify that we have appropriate winsock funtionality */
- err = CheckWinsockVersion ();
- if (err==0) {
- /* Allocate required resources */
- StopEvent = WSACreateEvent ();
- if (StopEvent!=NULL) {
- /* Win32 console input (and output) does not provide
- for asyncronous (overlapped) operations, so we create
- a thread dedicated to handling user input */
- HInputThread = CreateThread (NULL, 0,
- InputThread,
- NULL,
- CREATE_SUSPENDED,
- &idThread);
- if (HInputThread!=NULL) {
- /* Process command line arguments */
- NumSessions = 0; /* Zero out number of multicast sessions */
- for (i=1; i<argc; i++) {
- /* First single out options */
- if (_tcsicmp (argv[i], TEXT ("-l"))==0) {
- Loopback = FALSE;
- }
- else if (_tcsicmp (argv[i], TEXT ("-r"))==0) {
- ReuseAddress = FALSE;
- }
- else if (_tcsicmp (argv[i], TEXT ("-j"))==0) {
- UseWSAJoin = FALSE;
- }
- else if (_tcsicmp (argv[i], TEXT ("-t"))==0) {
- if (++i<argc) {
- LPSTR endp; /* End of string pointer */
- McastTTL = _tcstoul (argv[i], &endp, 10);
- if ((McastTTL==0) || (*endp!=0)) {
- err = ERROR_INVALID_PARAMETER;
- _tprintf (TEXT ("Invalid time to live value.\n"));
- break;
- }
- }
- else {
- err = ERROR_INVALID_PARAMETER;
- _tprintf (TEXT ("Time to live value must be specified.\n"));
- break;
- }
- }
- else {
- /* Not an option. must be an address specification */
- err = ParseAddress (argv[i], &ctx);
- if (err==0) {
- /* Insert new context is the list */
- ctx->next = CtxList;
- CtxList = ctx;
- NumSessions += 1;
- }
- }
- }
-
- if (err==0) {
- /* Prepare to reverse the list (to restore
- command-line set processing order */
- ctx = CtxList;
- CtxList = NULL;
- while (ctx!=NULL) {
- /* Join multicast session */
- err = JoinSession (ctx);
- if (err==0) {
- /* Post asynchronous receive request
- on the multicast socket */
- err = PostReceiveRequest (ctx);
- if (err==0) {
- /* Reverse the context list */
- temp = ctx;
- ctx = ctx->next;
- temp->next = CtxList;
- CtxList = temp;
- continue;
- }
- else {
- _tprintf (
- TEXT ("Could not start receive for %s (err: %d)\n"),
- ctx->str, err);
- closesocket (ctx->s);
- }
- }
- break;
- }
-
-
- if (err==0) {
- /* Resume (start) input thread */
- ResumeThread (HInputThread);
-
- /* Allow normal interrupt processing */
- SetConsoleCtrlHandler (NULL, FALSE);
- /* Intercept interrupts to cleanup properly */
- SetConsoleCtrlHandler (CtrlHandler, TRUE);
-
-
- /* Allow processing of received packets till
- stop event is signalled or wait error
- occurs */
- while (TRUE) {
- /* Wait alertably to let completion routines
- run */
- DWORD status = WaitForSingleObjectEx (
- StopEvent,
- INFINITE,
- TRUE);
- switch (status) {
- case WAIT_IO_COMPLETION:
- /* Continue processing IO completion */
- continue;
- case WAIT_OBJECT_0:
- default:
- /* Break out in case of stop signal or
- error */
- break;
- }
- break;
- }
- }
- else {
- /* Dispose of all unprocessed sockets contexts
- (open sockets are processed below in non-
- error case) */
- while (ctx!=NULL) {
- temp = ctx;
- ctx = ctx->next;
- free (temp);
- }
- }
-
- /* Close all open sockets to force completion
- of all outstanding IO requests */
- ctx = CtxList;
- while (ctx!=NULL) {
- closesocket (ctx->s);
- ctx = ctx->next;
- }
-
- /* Keep this thread around until all posted requests
- complete */
- while (NumPendingRcvs>0)
- /* Sleep alertably to let completion routines
- run */
- SleepEx (INFINITE, TRUE);
-
- /* Dispose of all remaining contexts */
- while (CtxList!=NULL) {
- ctx = CtxList;
- CtxList = CtxList->next;
- free (ctx);
- }
- }
- /* Close input thread handle */
- CloseHandle (HInputThread);
- }
- else {
- _tprintf (TEXT ("Could not create input thread.\n"));
- err = GetLastError ();
- }
- /* Close stop event */
- WSACloseEvent (StopEvent);
- }
- else {
- _tprintf (TEXT ("Could not create stop event.\n"));
- err = GetLastError ();
- }
- /* Cleanup winsock DLL */
- WSACleanup ();
- }
-
- /* Restore screen buffer to original size */
- SetConsoleScreenBufferSize (HStdout, OldScreen.dwSize);
-
- /* Close input handles */
- CloseHandle (HStdout);
- if (HStdin != INVALID_HANDLE_VALUE)
- CloseHandle (HStdin);
-
-
- /* Print usage message if we suspect that user passed in
- invalid parameter */
- if (err==ERROR_INVALID_PARAMETER) {
- Usage:
- _tprintf (
- TEXT("Multi-Chat - join in and chat on multiple multicast addresses.\n")
- TEXT("Usage:\n")
- TEXT(" %s [options] mc_addr[:port][,if_addr] [...]\n")
- TEXT("Where:\n")
- TEXT(" mc_addr - multicast address to join,\n")
- TEXT(" port - port on which to join,\n")
- TEXT(" if_addr - local interface to join on,\n")
- TEXT(" ... - specify as many addresses as can feet on your screen.\n")
- TEXT("Options:\n")
- TEXT(" -? - print this message (must be first, rest is ignored),\n")
- TEXT(" -l - don't loopback my own messages,\n")
- TEXT(" -r - dissallow reuse of allocated addresses (ports),\n")
- TEXT(" -j - use IP_ADD_MEMBERSHIP to join instead of WSAJoinLeaf,\n")
- TEXT(" -t ttl - override defult time to live value with ttl.\n\n")
- TEXT("Example:\n")
- TEXT(" %s 224.0.0.39 224.0.0.40:1001 224.0.0.41:1002,157.55.88.254\n"),
- argv[0],
- argv[0]);
- }
-
- return err;
- }
-
-
-
- /*********************************************************************
- * FUNCTION: CheckWinsockVersion *
- * *
- * PURPOSE: Verifies that proper version of WinSock DLL is installed *
- * on the system. *
- * *
- * INPUT: None *
- * *
- * RETURNS: 0 if WinSock DLL is appropriate for our purposes, *
- * WSAVERNOTSUPPORTED or other system error code if WinSock *
- * DLL cannot support required functions or cannot be *
- * loaded. *
- * *
- *********************************************************************/
- int
- CheckWinsockVersion (
- VOID
- ) {
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- /* Asynchronous IO and multicast semantics we use supported
- starting only with WinSock 2.0 */
- wVersionRequested = MAKEWORD (2, 0);
-
- err = WSAStartup (wVersionRequested, &wsaData);
- if (err==0) {
-
- /* Confirm that the WinSock DLL supports 2.0.
- Note that if the DLL supports versions greater
- than 2.0 in addition to 2.0, it will still return
- 2.0 in wVersion since that is the version we
- requested. */
-
- if ((LOBYTE (wsaData.wVersion)==2)
- && (HIBYTE (wsaData.wVersion)==0)) {
- /* The WinSock DLL is acceptable. Proceed. */
- return 0;
- }
-
- WSACleanup( );
- err = WSAVERNOTSUPPORTED;
- }
-
- /* Tell the user that we couldn't find a usable
- WinSock DLL.*/
- _tprintf (TEXT ("WinSock DLL does not support requested API version.\n"));
-
-
- return err;
- }
-
-
-
- /*********************************************************************
- * FUNCTION: ParseAddress *
- * *
- * PURPOSE: Parses multicast and interface addresses in the string *
- * and allocates and initializes context block for multicast*
- * session. *
- * *
- * INPUT: Address string and vairable to place allocated context. *
- * *
- * RETURNS: 0 if addresses were successfully processed and context *
- * allocated and returned, *
- * ERROR_INVALID_PARAMETER or WSAEINVAL if specified *
- * address could not be parsed, *
- * ERROR_OUT_OF_MEMORY if there is not enough resources to *
- * allocate context for multicast session. *
- * *
- *********************************************************************/
- int
- ParseAddress (
- LPTSTR argv,
- PMCAST_CONTEXT *ctx
- ) {
- struct sockaddr_in addr; /* Address structure returned by WinSock DLL */
- int len; /* Address length returned by the WinSock DLL */
- LPTSTR pszAddr;/* Pointer to the address to parse */
- int err = 0;/* Error code */
-
- *ctx = (PMCAST_CONTEXT)malloc (sizeof (MCAST_CONTEXT));
- if (*ctx==NULL) {
- _tprintf (TEXT ("Not enough memory to allocate session context.\n"));
- return ERROR_NOT_ENOUGH_MEMORY;
- }
-
- (*ctx)->str = argv;
-
- /* Separate out multicast address and port */
- pszAddr = _tcstok (argv, TEXT(","));
-
- /* Call WinSock2 DLL to parse the address */
- len = sizeof(addr);
- err = WSAStringToAddress (pszAddr,
- AF_INET,
- NULL,
- (LPSOCKADDR)&addr,
- &len);
- if (err==0) {
- (*ctx)->addr = addr.sin_addr;
- /* Get the interface address if it is provided*/
- pszAddr = _tcstok (NULL, TEXT(","));
- if (pszAddr!=NULL) {
- /* Save the pointer to interface address text*/
- (*ctx)->ifstr = pszAddr;
- len = sizeof(addr);
- err = WSAStringToAddress (pszAddr,
- AF_INET,
- NULL,
- (LPSOCKADDR)&addr,
- &len);
- if (err==0)
- (*ctx)->ifad = addr.sin_addr;
- else {
- err = WSAGetLastError ();
- _tprintf (TEXT ("Could not parse interface address: %s"), pszAddr);
- }
- }
- else {
- /* Use default address if it was not specified */
- (*ctx)->ifad.S_un.S_addr = INADDR_ANY;
- (*ctx)->ifstr = pszAddr;
- }
- }
- else {
- err = WSAGetLastError ();
- _tprintf (TEXT ("Could not parse multicast address: %s\n"), pszAddr);
- }
-
- return err;
- }
-
- /*********************************************************************
- * FUNCTION: JoinSession *
- * *
- * PURPOSE: Joins in multicast session. *
- * *
- * INPUT: Multicast session context. *
- * *
- * RETURNS: 0 if operation succeds, *
- * WinSock error code if it fails. *
- * *
- *********************************************************************/
- int
- JoinSession (
- PMCAST_CONTEXT ctx
- ) {
- int err = 0;/* Error code */
- struct sockaddr_in addr; /* Address structure for WinSock DLL calls */
- int len; /* Address length for WinSock DLL calls */
-
- /* Allocate UDP socket */
- if (UseWSAJoin)
- ctx->s = WSASocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP,
- NULL, /* Protocol Info */
- 0, /* Group */
- WSA_FLAG_OVERLAPPED
- | WSA_FLAG_MULTIPOINT_C_LEAF
- | WSA_FLAG_MULTIPOINT_D_LEAF
- );
- else
- ctx->s = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (ctx->s!=INVALID_SOCKET) {
- /* Break out of this pseudo-loop in case something fails*/
- do {
-
- if (ReuseAddress) {
- err = setsockopt (ctx->s,
- SOL_SOCKET,
- SO_REUSEADDR,
- (char *)&ReuseAddress,
- sizeof (ReuseAddress));
- if (err!=0) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not set reuse address option for %s (err: %d).\n"),
- ctx->str, err);
- break;
- }
-
- }
-
- /* Bind to specified port on the interface of user's choice
- (can be INADDR_ANY - default interface - if user did not
- specify any) */
- addr.sin_family = AF_INET;
- addr.sin_port = ctx->port;
- addr.sin_addr = ctx->ifad;
- err = bind (ctx->s,
- (PSOCKADDR)&addr,
- sizeof (addr));
- if (err!=0) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not bind to address %s (err: %d).\n"),
- ctx->ifstr, err);
- break;
- }
- /* Get back address we actually bound to (if user did
- not specify the port, we'll learn the number assigned
- by the system) */
- len = sizeof (addr);
- err = getsockname (ctx->s,
- (PSOCKADDR)&addr,
- &len);
- if (err!=0) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not recover actual socket address for %s (err: %d).\n"),
- ctx->str, err);
- break;
- }
- ctx->port = addr.sin_port;
-
-
- if (UseWSAJoin) {
- DWORD cbRet;
- SOCKET s;
-
- /* Use WinSock 2.0 specific API to join multicast session */
- if (McastTTL!=0) {
- err = WSAIoctl (ctx->s, /* socket */
- SIO_MULTICAST_SCOPE, /* code */
- &McastTTL, /* input */
- sizeof (McastTTL), /* size */
- NULL, /* output */
- 0, /* size */
- &cbRet, /* bytes returned */
- NULL, /* overlapped */
- NULL /* completion routine */
- );
- if (err!=0) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not set multicast TTL for %s (err: %d).\n"),
- ctx->str, err);
- break;
- }
- }
-
- /* Setup address to join to (just the multicast address
- itself, the rest of the structure is OK as returned
- from getsockname) */
- addr.sin_addr = ctx->addr;
- s = WSAJoinLeaf (ctx->s,
- (PSOCKADDR)&addr,
- sizeof (addr),
- NULL,
- NULL,
- NULL,
- NULL,
- JL_BOTH);
- if (s==INVALID_SOCKET) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not join multicast session %s (err: %d).\n"),
- ctx->str, err);
- break;
- }
-
- }
- else {
- struct ip_mreq mreq;
- /* Use socket options to join multicast session */
- if (McastTTL!=0) {
- err = setsockopt (ctx->s,
- IPPROTO_IP,
- IP_MULTICAST_TTL,
- (char *)&McastTTL,
- sizeof (McastTTL));
- if (err!=0) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not set multicast TTL for %s (err: %d).\n"),
- ctx->str, err);
- break;
- }
- }
-
-
- mreq.imr_interface = ctx->ifad;
- err = setsockopt (ctx->s,
- IPPROTO_IP,
- IP_MULTICAST_IF,
- (char *)&mreq.imr_interface,
- sizeof (mreq.imr_interface));
- if (err!=0) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not set default multicast interface %s (err: %d).\n"),
- ctx->str, err);
- break;
- }
-
- mreq.imr_multiaddr = ctx->addr;
- mreq.imr_interface = ctx->ifad;
- err = setsockopt (ctx->s,
- IPPROTO_IP,
- IP_ADD_MEMBERSHIP,
- (char *)&mreq,
- sizeof (mreq));
- if (err!=0) {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not join multicast session %s (err: %d).\n"),
- ctx->str, err);
- break;
- }
- }
- /* If we got here, join succeded */
- return 0;
- }
- while (FALSE);
- /* Failed something, close the socket */
- closesocket (ctx->s);
- }
- else {
- err = WSAGetLastError ();
- _tprintf (
- TEXT ("Could not create socket for %s (err: %d)\n"),
- ctx->str, err);
- }
- return err;
- }
-
-
- /*********************************************************************
- * FUNCTION: PostReceiveRequest *
- * *
- * PURPOSE: Posts asynchronous receive request on the socket. *
- * *
- * INPUT: Multicast session context. *
- * *
- * RETURNS: 0 if operation succeds, *
- * WinSock error code if it fails. *
- * *
- *********************************************************************/
- int
- PostReceiveRequest (
- PMCAST_CONTEXT ctx
- ) {
- int err=0; /* Error code */
- WSABUF buf[1]; /* Receive buffer array */
- ULONG rcvd; /* Number of bytes recived */
- ULONG flags=0; /* Receive flags */
- /* We use completion routing, so event field can
- be utilized for additional parameter */
- ctx->ovlp.hEvent = (HANDLE)ctx;
- ctx->fromlen = sizeof (ctx->from);
- buf[0].buf = &ctx->buf[0];
- buf[0].len = sizeof (ctx->buf);
- err = WSARecvFrom (ctx->s,
- buf,
- 1,
- &rcvd,
- &flags,
- (PSOCKADDR)&ctx->from,
- &ctx->fromlen,
- &ctx->ovlp,
- RecvCompletion
- );
- if ((err==0)
- || ((err=WSAGetLastError ())==WSA_IO_PENDING)){
- /* Increment counter of pending receives */
- NumPendingRcvs += 1;
- return 0;
- }
-
- return err;
- }
-
-
-
- /*********************************************************************
- * FUNCTION: RecvCompletion *
- * *
- * PURPOSE: Completion routine, called by the WinSock DLL once *
- * asynchronous receive operation completes. *
- * *
- * INPUT: dwError - operation status (0 - success), *
- * cbTransferred - number of bytes received), *
- * lpOverlapped - overlapped structure specifed when receive*
- * was initiated, *
- * dwFlags - operation specific flags. *
- * *
- * RETURNS: nothing. *
- * *
- *********************************************************************/
- void CALLBACK
- RecvCompletion(
- IN DWORD dwError,
- IN DWORD cbTransferred,
- IN LPWSAOVERLAPPED lpOverlapped,
- IN DWORD dwFlags
- ) {
- /* Session context */
- PMCAST_CONTEXT ctx = (PMCAST_CONTEXT)lpOverlapped->hEvent;
- TCHAR szAddr[128]; /* Peer address */
- CHAR buffer[1024]; /* Output buffer */
- DWORD len; /* Address string length from WinSock */
- int err; /* Error code */
-
- /* Decrement counter of pending receives */
- NumPendingRcvs -= 1;
-
- switch (dwError) {
- /* If we received any data, dump them */
- case 0:
- case WSAEMSGSIZE:
- /* First convert peer address to string */
- len = sizeof (szAddr)/sizeof (szAddr[0]);
- err = WSAAddressToString (
- (PSOCKADDR)&ctx->from,
- ctx->fromlen,
- NULL,
- szAddr,
- &len);
- if (err==0) {
- #if defined(UNICODE) || defined (_UNICODE)
- len = sprintf (buffer, "From: %ls (%d bytes).",
- szAddr, cbTransferred);
- #else
- len = sprintf (buffer, "From: %s (%d bytes).",
- szAddr, cbTransferred);
- #endif
- }
- else
- len = sprintf (buffer, "From: ????(%d bytes).",
- cbTransferred);
- /* Write peer address */
- WriteMiniConsoleA (&ctx->coord, buffer, len);
- /* Write peer message */
- WriteMiniConsoleA (&ctx->coord, ctx->buf, cbTransferred);
-
-
- default:
- if (dwError!=0) {
- /* Write completion error message */
- len = sprintf (buffer,
- "Receive completed with error %d.", dwError);
- WriteMiniConsoleA (&ctx->coord, buffer, len);
- }
- err = PostReceiveRequest (ctx);
- if (err!=0) {
- /* Write post error message */
- len = sprintf (buffer,
- "Could not repost receive request (err %d).", err);
- WriteMiniConsoleA (&ctx->coord, buffer, len);
- }
- break;
-
- /* The following error codes indicate that we were interrupted
- by the user and should just let it exit out */
- case WSAENOTSOCK:
- case WSAEINTR:
- case WSA_OPERATION_ABORTED:
- break;
- }
- }
-
- /*********************************************************************
- * FUNCTION: CtrlHandler *
- * *
- * PURPOSE: Console control handler, called when user generates *
- * interrupt from the keyboard. *
- * *
- * INPUT: Flags that indicate what kind of user action generated *
- * the interrupt. *
- * *
- * RETURNS: TRUE - interrupt is handled by the program, *
- * FALSE - system should handle it. *
- * *
- *********************************************************************/
- BOOL CALLBACK
- CtrlHandler(
- DWORD dwCtrlType
- ) {
- HANDLE local;
- INPUT_RECORD Input;
- DWORD written;
- if (HStdin!=INVALID_HANDLE_VALUE) {
- /* Invalidate input handle */
- local = HStdin;
- HStdin = INVALID_HANDLE_VALUE;
- /* Remove our control handler from the chain */
- SetConsoleCtrlHandler (CtrlHandler, FALSE);
- /* Disallow further control operations */
- SetConsoleCtrlHandler (NULL, TRUE);
- /* Expedite exiting */
- SetConsoleMode (local, 0);
- /* Write return */
- Input.EventType = KEY_EVENT;
- Input.Event.KeyEvent.bKeyDown = TRUE;
- Input.Event.KeyEvent.wRepeatCount = 10;
- Input.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
- Input.Event.KeyEvent.wVirtualScanCode = 1;
- Input.Event.KeyEvent.uChar.AsciiChar = '\015';
- Input.Event.KeyEvent.dwControlKeyState = 0;
- WriteConsoleInput (local,
- &Input,
- 1,
- &written);
- /*Close the handle */
- CloseHandle (local);
- /* Tell that we are going to handle this ourselves */
- return TRUE;
- }
- else
- return FALSE;
- }
-
-
-
-
- /*********************************************************************
- * FUNCTION: WriteMiniConsoleA *
- * *
- * PURPOSE: Writes output to mini-console reserved for each multicast*
- * session and scrolls is as appropriate. *
- * *
- * INPUT: coord - coordinates to start writing at (updated upon *
- * completion, *
- * output - output string, *
- * len - length of output string. *
- * *
- * RETURNS: Nothing. *
- * *
- *********************************************************************/
- VOID
- WriteMiniConsoleA (
- IN OUT COORD *coord,
- LPSTR output,
- DWORD len
- ) {
- DWORD i; /* Current position in output buffer */
- DWORD written; /* Number of charactes written by
- console output function */
-
- for (i=0; i<len; i+=MINI_COLS) {
- /* Write as many as fits on one line of the mini-console */
- WriteConsoleOutputCharacterA (HStdout,
- &output[i],
- (i+MINI_COLS<len) ? MINI_COLS : len-i,
- *coord,
- &written);
- /* Scroll mini-console if we reached the last line */
- if (coord->Y==StdScreen.dwSize.Y-INPUT_ROWS-1) {
- SMALL_RECT scroll;
- SMALL_RECT clip;
- COORD origin;
- CHAR_INFO fill;
-
- origin.X = scroll.Left = clip.Left = coord->X;
- origin.Y = clip.Top = HEADER_ROWS;
- scroll.Top = clip.Top+1;
- scroll.Right = clip.Right = coord->X+MINI_COLS-1;
- scroll.Bottom = clip.Bottom = StdScreen.dwSize.Y-INPUT_ROWS-1;
- fill.Char.AsciiChar = ' ';
- fill.Attributes = StdScreen.wAttributes;
-
- ScrollConsoleScreenBuffer (HStdout,
- &scroll,
- &clip,
- origin,
- &fill);
- }
- else /* Otherwise, advance to the next line */
- coord->Y += 1;
- }
- }
-
- /*********************************************************************
- * FUNCTION: PaintScreen *
- * *
- * PURPOSE: Draws headers of mini-consoles associated with each *
- * multicast session. *
- * *
- * INPUT: None *
- * *
- * RETURNS: Nothing. *
- * *
- *********************************************************************/
- VOID
- PaintScreen (
- VOID
- ) {
- PMCAST_CONTEXT ctx; /* Current session context */
- COORD sz; /* Screen size */
- COORD pos; /* Current position on the screen */
- struct sockaddr_in addr; /* Socket address structure for conversions */
- TCHAR szAddr[MINI_COLS+1];
- /* Converted socket address string */
- TCHAR buffer[128];
- /* Output string buffer */
- DWORD len; /* Number of bytes written/converted */
- int err; /* Error code */
-
- /* Setup the screen according to number of
- multicast sockets we have */
- sz.X = (MINI_COLS+1)*NumSessions;
-
- /* Can not be less than the current window size */
- if (sz.X<OldScreen.srWindow.Right-OldScreen.srWindow.Left+1)
- sz.X = OldScreen.srWindow.Right-OldScreen.srWindow.Left+1;
- sz.Y = OldScreen.srWindow.Bottom-OldScreen.srWindow.Top+1;
- SetConsoleScreenBufferSize (HStdout, sz);
- GetConsoleScreenBufferInfo (HStdout, &StdScreen);
-
- /* Clear the screen */
- FillConsoleOutputAttribute (HStdout,
- StdScreen.wAttributes,
- sz.X*sz.Y,
- Zero,
- &len);
- FillConsoleOutputCharacterA (HStdout,
- ' ',
- sz.X*sz.Y,
- Zero,
- &len);
-
- /* Paint header part of each mini console
- reserved for each of the addresses in
- the list */
- addr.sin_family = AF_INET;
-
- for (ctx=CtxList, pos.X=0; ctx!=NULL; ctx=ctx->next, pos.X+=MINI_COLS+1) {
- /* Set initial coordinates of the mini-console */
- ctx->coord.Y = HEADER_ROWS;
- ctx->coord.X = pos.X;
- /* If mini-console fits on the screen */
- if (pos.X+MINI_COLS<StdScreen.dwSize.X) {
- /* Convert multicast address to string */
- addr.sin_port = ctx->port;
- addr.sin_addr = ctx->addr;
- len = sizeof(szAddr);
- err = WSAAddressToString ((PSOCKADDR)&addr,
- sizeof (addr),
- NULL,
- szAddr,
- &len);
- if (err==0)
- len = _stprintf (buffer,
- TEXT("A:%*s"),
- MINI_COLS-2, szAddr);
- else
- len = _stprintf (buffer,
- TEXT("A:??? (error %ld)"),
- WSAGetLastError ());
- /* Write it out */
- pos.Y = 0;
- WriteConsoleOutputCharacter (
- HStdout,
- buffer,
- len,
- pos,
- &len);
-
- /* Invert header color */
- FillConsoleOutputAttribute (
- HStdout,
- (WORD)(StdScreen.wAttributes
- ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
- | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
- MINI_COLS,
- pos,
- &len);
-
-
- /* Convert multicast address to string (if it is not default)*/
- if (ctx->ifad.S_un.S_addr != INADDR_ANY) {
- addr.sin_addr = ctx->ifad;
- addr.sin_port = 0; /* Remove port number from output */
- len = sizeof(szAddr);
- err = WSAAddressToString ((PSOCKADDR)&addr,
- sizeof (addr),
- NULL,
- szAddr,
- &len);
- if (err==0)
- len = _stprintf (buffer,
- TEXT("I-face:%*s"),
- MINI_COLS-7, szAddr);
- else
- len = _stprintf (buffer,
- TEXT("I-face:??? (error %ld)"),
- WSAGetLastError ());
- }
- else
- len = _stprintf (buffer,
- TEXT("I-face: Default"));
- /* Write it out */
- pos.Y = 1;
- WriteConsoleOutputCharacter (
- HStdout,
- buffer,
- len,
- pos,
- &len);
-
- /* Invert header color */
- FillConsoleOutputAttribute (
- HStdout,
- (WORD)(StdScreen.wAttributes
- ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
- | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
- MINI_COLS,
- pos,
- &len);
-
- }
- }
-
- /* Clear and invert input row */
- pos.X = 0;
- pos.Y = StdScreen.dwSize.Y-INPUT_ROWS;
- FillConsoleOutputCharacterA (HStdout,
- ' ',
- StdScreen.dwSize.X,
- pos,
- &len);
-
- FillConsoleOutputAttribute (
- HStdout,
- (WORD)(StdScreen.wAttributes
- ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
- | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
- StdScreen.dwSize.X,
- pos,
- &len);
-
- /* Write help message in status line */
- pos.X = 0;
- pos.Y = StdScreen.dwSize.Y-INPUT_ROWS+1;
- WriteConsoleOutputCharacter (HStdout,
- HELP_MSG,
- (HELP_MSG_LEN<=StdScreen.dwSize.X)
- ? HELP_MSG_LEN
- : StdScreen.dwSize.X,
- pos,
- &len);
- }
-
-
- /*********************************************************************
- * FUNCTION: InputThread *
- * *
- * PURPOSE: Reads user message and sends it on all multicast sessions*
- * *
- * INPUT: None *
- * *
- * RETURNS: 0. *
- * *
- *********************************************************************/
- DWORD WINAPI
- InputThread (
- LPVOID param
- ) {
- CHAR buffer[1600]; /* Input buffer */
- TCHAR szStatus[128]; /* Status string */
- COORD posInp, posStat;/* Input and status line coordinates */
- DWORD written,read; /* Number of bytes written to the screen */
- PMCAST_CONTEXT ctx; /* Current session context */
- struct sockaddr_in addr; /* Destination address */
- int err; /* Error code */
-
- PaintScreen ();
- addr.sin_family = AF_INET;
-
- /* Setup input row positions */
- posInp.X = 0;
- posInp.Y = StdScreen.dwSize.Y-INPUT_ROWS;
- posStat.X = 0;
- posStat.Y = StdScreen.dwSize.Y-INPUT_ROWS+1;
-
- /* Place cursor in the first input row */
- SetConsoleCursorPosition (HStdout, posInp);
-
- /* Read user input */
- while (ReadFile (HStdin, buffer, sizeof (buffer), &read, NULL)
- && (read>0)
- && (HStdin!=INVALID_HANDLE_VALUE)) {
- /* Cleanup status line */
- posStat.X = 0;
- FillConsoleOutputCharacterA (HStdout,
- ' ',
- StdScreen.dwSize.X,
- posStat,
- &written);
-
- /* Send message on all multicast sessions in the list */
- for (ctx=CtxList; ctx!=NULL; ctx=ctx->next, posStat.X+=MINI_COLS+1) {
- addr.sin_addr = ctx->addr;
- addr.sin_port = ctx->port;
-
- err = sendto (ctx->s,
- buffer,
- read,
- 0,
- (PSOCKADDR)&addr,
- sizeof (addr));
- if (err>=0)
- written = _stprintf (szStatus,
- TEXT ("%d bytes sent."),
- err);
- else
- written = _stprintf (szStatus,
- TEXT ("Send failed (err %d)."),
- err);
- WriteConsoleOutputCharacter (HStdout,
- szStatus,
- written,
- posStat,
- &written);
- }
-
- /* Cleanup input line again */
- FillConsoleOutputAttribute (HStdout,
- (WORD)(StdScreen.wAttributes
- ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
- | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
- StdScreen.dwSize.X,
- posInp,
- &written);
-
- FillConsoleOutputCharacterA (HStdout,
- ' ',
- StdScreen.dwSize.X,
- posInp,
- &written);
- SetConsoleCursorPosition (HStdout, posInp);
- }
- /* Cleanup status line befor exiting */
- posStat.X = 0;
- FillConsoleOutputCharacterA (HStdout,
- ' ',
- StdScreen.dwSize.X,
- posStat,
- &written);
- SetConsoleCursorPosition (HStdout, posInp);
-
- WSASetEvent (StopEvent);
- return 0;
- }